home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-01
/
ohlutil.zip
/
MV.C
< prev
next >
Wrap
C/C++ Source or Header
|
1990-06-22
|
11KB
|
479 lines
/* mv -- move or rename files
Copyright (C) 1986, 1989, 1990 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
/* Options:
-i (+interactive)
Causes mv to require confirmation from the user before
performing any move that would destroy an existing file.
-f (+force)
Causes mv to assume a 'y' answer to all questions it would normally ask
(and not ask the questions).
If -f is not given, the user is queried when a move would
destroy an existing file whose mode prohibits writing, unless
stdin is not a tty, in which case the file is skipped.
This test is slightly different from the test 4.2BSD mv makes; 4.2 mv
merely checks whether the user can write the file. This actually checks
the write bits in the mode. The difference shows when super-user tries
to mv onto a file with no write bits set.
-v (+verbose)
List the name of each file as it is moved, and the name it is moved to.
Written by Mike Parker and David MacKenzie */
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include "system.h"
#include "getopt.h"
#include "backupfile.h"
#ifdef STDC_HEADERS
#include <stdlib.h>
#else
char *getenv ();
extern int errno;
#endif
enum backup_type get_version ();
int copy ();
int do_move ();
int movefile ();
int yesno ();
void error ();
void strip_trailing_slashes ();
void usage ();
/* The name this program was run with. */
char *program_name;
/* If nonzero, query the user before overwriting files. */
int interactive;
/* If nonzero, override as much protection as possible. */
int force;
/* If nonzero, list each file as it is moved. */
int verbose;
/* If nonzero, stdin is not a tty. */
int stdin_not_tty;
/* Return nonzero if FN is a directory or a symlink to a directory,
zero if not. */
int
isdir (fn)
char *fn;
{
struct stat stats;
return (stat (fn, &stats) == 0 && (stats.st_mode & S_IFMT) == S_IFDIR);
}
struct option long_options[] =
{
{"backup", 0, NULL, 'b'},
{"force", 0, &force, 1},
{"interactive", 0, &interactive, 1},
{"suffix", 1, NULL, 'S'},
{"verbose", 0, &verbose, 1},
{"version-control", 1, NULL, 'V'},
{NULL, 0, NULL, 0}
};
void
main (argc, argv)
int argc;
char **argv;
{
int c;
int ind;
int errors;
int make_backups = 0;
char *version;
version = getenv ("SIMPLE_BACKUP_SUFFIX");
if (version)
simple_backup_suffix = version;
version = getenv ("VERSION_CONTROL");
program_name = argv[0];
interactive = force = verbose = 0;
errors = 0;
while ((c = getopt_long (argc, argv, "bfivS:V:", long_options, &ind))
!= EOF)
{
if (c == 0 && long_options[ind].flag == NULL)
c = long_options[ind].val;
switch (c)
{
case 0:
break;
case 'b':
make_backups = 1;
break;
case 'f':
force = 1;
break;
case 'i':
interactive = 1;
break;
case 'v':
verbose = 1;
break;
case 'S':
simple_backup_suffix = optarg;
break;
case 'V':
version = optarg;
break;
default:
usage ();
}
}
if (argc < optind + 2)
usage ();
if (make_backups)
backup_type = get_version (version);
strip_trailing_slashes (argv[argc - 1]);
if (argc > optind + 2 && !isdir (argv[argc - 1]))
error (1, 0, "when moving multiple files, last argument must be a directory");
if (interactive)
force = 0;
stdin_not_tty = !isatty (0);
/* Move each arg but the last onto the last. */
for (; optind < argc - 1; ++optind)
errors |= movefile (argv[optind], argv[argc - 1]);
exit (errors);
}
/* Move file FROM onto TO. Handles the case when TO is a directory.
Return 0 if successful, 1 if an error occurred. */
int
movefile (from, to)
char *from;
char *to;
{
strip_trailing_slashes (from);
if (isdir (to))
{
/* Target is a directory; build full target filename. */
char *cp;
char *newto;
cp = rindex (from, '/');
if (cp)
cp++;
else
cp = from;
newto = (char *) alloca (strlen (to) + 1 + strlen (cp) + 1);
sprintf (newto, "%s/%s", to, cp);
return do_move (from, newto);
}
else
return do_move (from, to);
}
struct stat to_stats, from_stats;
/* Move FROM onto TO. Handles cross-filesystem moves.
If TO is a directory, FROM must be also.
Return 0 if successful, 1 if an error occurred. */
int
do_move (from, to)
char *from;
char *to;
{
char *to_backup = NULL;
if (lstat (from, &from_stats) != 0)
{
error (0, errno, "%s", from);
return 1;
}
if (verbose)
printf (" %s -> %s\n", from, to);
if (lstat (to, &to_stats) == 0)
{
if (from_stats.st_dev == to_stats.st_dev
&& from_stats.st_ino == to_stats.st_ino)
{
error (0, 0, "`%s' and `%s' are the same file", from, to);
return 1;
}
if ((to_stats.st_mode & S_IFMT) == S_IFDIR)
{
error (0, 0, "%s: cannot overwrite directory", to);
return 1;
}
if (interactive)
{
fprintf (stderr, "%s: replace `%s'? ", program_name, to);
if (!yesno ())
return 0;
}
else if (!force)
{
int may_overwrite;
/* Treat the file as nonwritable if it lacks write permission bits,
even if we are root. */
#ifdef S_IFLNK
if ((to_stats.st_mode & S_IFMT) == S_IFLNK)
may_overwrite = 1;
else
#endif
may_overwrite = eaccess_stat (&to_stats, W_OK) == 0
&& (to_stats.st_mode & 0222);
if (!may_overwrite)
{
if (stdin_not_tty)
{
error (0, 0, "%s: no write permission", to);
return 1;
}
fprintf (stderr, "%s: override mode %04o for `%s'? ",
program_name, to_stats.st_mode & 0777, to);
if (!yesno ())
return 0;
}
}
if (backup_type != none)
{
to_backup = find_backup_file_name (to);
if (to_backup == NULL)
error (1, 0, "virtual memory exhausted");
if (rename (to, to_backup))
{
if (errno != ENOENT)
{
error (0, errno, "cannot backup `%s'", to);
free (to_backup);
return 1;
}
else
{
free (to_backup);
to_backup = NULL;
}
}
}
}
else if (errno != ENOENT)
{
error (0, errno, "%s", to);
return 1;
}
if (rename (from, to) == 0)
{
if (to_backup)
free (to_backup);
return 0;
}
if (errno != EXDEV)
{
error (0, errno, "cannot move `%s' to `%s'", from, to);
goto un_backup;
}
/* rename failed on cross-filesystem link. Copy the file instead. */
if (copy (from, to))
goto un_backup;
if (to_backup)
free (to_backup);
if (unlink (from))
{
error (0, errno, "cannot remove `%s'", from);
return 1;
}
return 0;
un_backup:
if (to_backup)
{
if (rename (to_backup, to))
error (0, errno, "cannot un-backup `%s'", to);
free (to_backup);
}
return 1;
}
/* Copy file FROM onto file TO.
Return 1 if an error occurred, 0 if successful. */
int
copy (from, to)
char *from, *to;
{
int ifd;
int ofd;
char buf[1024 * 8];
int len; /* Number of bytes read into `buf'. */
if ((from_stats.st_mode & S_IFMT) != S_IFREG)
{
error (0, 0, "cannot move `%s' across filesystems: Not a regular file",
from);
return 1;
}
if (unlink (to) && errno != ENOENT)
{
error (0, errno, "cannot remove `%s'", to);
return 1;
}
ifd = open (from, O_RDONLY, 0);
if (ifd < 0)
{
error (0, errno, "%s", from);
return 1;
}
ofd = open (to, O_WRONLY | O_CREAT | O_TRUNC, 0777);
if (ofd < 0)
{
error (0, errno, "%s", to);
close (ifd);
return 1;
}
if (
#ifdef FCHMOD_MISSING
chmod (to, from_stats.st_mode & 0777)
#else
fchmod (ofd, from_stats.st_mode & 0777)
#endif
)
{
error (0, errno, "%s", to);
close (ifd);
close (ofd);
unlink (to);
return 1;
}
while ((len = read (ifd, buf, sizeof (buf))) > 0)
{
int wrote = 0;
char *bp = buf;
do
{
wrote = write (ofd, bp, len);
if (wrote < 0)
{
error (0, errno, "%s", to);
close (ifd);
close (ofd);
unlink (to);
return 1;
}
bp += wrote;
len -= wrote;
} while (len > 0);
}
if (len < 0)
{
error (0, errno, "%s", from);
close (ifd);
close (ofd);
unlink (to);
return 1;
}
close (ifd);
close (ofd);
/* Try to copy the old file's modtime and access time. */
{
struct utimbuf tv;
tv.actime = from_stats.st_atime;
tv.modtime = from_stats.st_mtime;
if (utime (to, &tv))
{
error (0, errno, "%s", to);
return 1;
}
}
return 0;
}
/* Read one line from standard input
and return nonzero if that line begins with y or Y,
otherwise return 0. */
int
yesno ()
{
int c;
int rv;
fflush (stderr);
c = getchar ();
rv = (c == 'y') || (c == 'Y');
while (c != EOF && c != '\n')
c = getchar ();
return rv;
}
/* Remove trailing slashes from PATH; they cause some system calls to fail. */
void
strip_trailing_slashes (path)
char *path;
{
int last;
last = strlen (path) - 1;
while (last > 0 && path[last] == '/')
path[last--] = '\0';
}
void
usage ()
{
fprintf (stderr, "\
Usage: %s [-bfiv] [-S backup-suffix] [-V {numbered,existing,simple}]\n\
[+backup] [+force] [+interactive] [+verbose] [+suffix backup-suffix]\n\
[+version-control {numbered,existing,simple}] source dest\n\
\n\
%s [-bfiv] [-S backup-suffix] [-V {numbered,existing,simple}]\n\
[+backup] [+force] [+interactive] [+verbose] [+suffix backup-suffix]\n\
[+version-control {numbered,existing,simple}] source... directory\n",
program_name, program_name);
exit (1);
}